/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/* 
 * Copyright (c) 2009 City University of Hong Kong
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License m_version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Author: Wilson Thong (wilsonwk@ee.cityu.edu.hk) 
 */
#include "ipv4-deflection-routing-helper.h"
#include "ns3/ipv4-list-routing.h"
#include "ns3/ptr.h"
#include "ns3/ref-count-base.h"
#include "ns3/names.h"

NS_LOG_COMPONENT_DEFINE ("Ipv4DeflectionRoutingHelper");

namespace ns3 {
namespace eecityu {

NS_OBJECT_ENSURE_REGISTERED (Ipv4Header);

// A function which always return true. It is used to select all nodes for
// installing deflection routing
bool
ChooseAll (Ptr<Node> p) 
{
  return true;
}

UniformVariable Ipv4DeflectionRoutingHelper::g_randSelector = UniformVariable ();

//TypeId 
//Ipv4DeflectionRoutingHelper::GetTypeId (void)
//{
//  static TypeId tid = TypeId ("ns3::eecityu::Ipv4DeflectionRoutingHelper")
//    .SetParent<ObjectBase> ()
//    .AddConstructor<Ipv4DeflectionRoutingHelper> ()
//    .AddTraceSource ("NodeSelected", "A node is selected for installing deflection routing protocol.",
//                     MakeTraceSourceAccessor (&Ipv4DeflectionRoutingHelper::m_nodeSelectedTrace))
//    .AddTraceSource ("NodeSkipped", "A node is skipped for installing deflection routing protocol",
//                     MakeTraceSourceAccessor (&Ipv4DeflectionRoutingHelper::m_nodeSkippedTrace))
//    ;
//  return tid;
//}
//
//TypeId 
//Ipv4DeflectionRoutingHelper::GetInstanceTypeId (void) const
//{
//  return GetTypeId ();
//}

Ipv4DeflectionRoutingHelper::Ipv4DeflectionRoutingHelper ()
{}

void 
Ipv4DeflectionRoutingHelper::PopulateRoutingTables ()
{
  Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
}

uint32_t 
Ipv4DeflectionRoutingHelper::PopulateRoutingTables (const BaseTopology& net)
{
  return Ipv4DeflectionRoutingHelper ().PopulateRoutingTablesImpl (net);
}

uint32_t 
Ipv4DeflectionRoutingHelper::PopulateRoutingTables (const BaseTopology& net, double selectedPb)
{
  return Ipv4DeflectionRoutingHelper ().PopulateRoutingTablesImpl (net, selectedPb);
}

uint32_t 
Ipv4DeflectionRoutingHelper::PopulateRoutingTables (
  const BaseTopology& net, const Callback<bool, Ptr<Node> >& selectedNodeCallback)
{
  return Ipv4DeflectionRoutingHelper ().PopulateRoutingTables (net, selectedNodeCallback);
}

uint32_t 
Ipv4DeflectionRoutingHelper::PopulateRoutingTablesImpl (const BaseTopology& net)
{
  NS_LOG_FUNCTION (&net);

  return PopulateRoutingTablesImpl ( net, MakeCallback (&ChooseAll) );
}

bool 
Ipv4DeflectionRoutingHelper::RandomNodeSelector (double r, Ptr<Node> n)
{
  NS_LOG_FUNCTION (r << n);

  return r >= g_randSelector.GetValue (0.0, 1.0);
}

uint32_t 
Ipv4DeflectionRoutingHelper::PopulateRoutingTablesImpl (const BaseTopology& net, double selectedPb)
{
  NS_LOG_FUNCTION (&net << selectedPb);

  // validing the range of 'selectPb'
  NS_ASSERT_MSG (selectedPb >= 0.0, "The probabaility of selecting a node cannot be smaller than 0.0!");
  NS_ASSERT_MSG (selectedPb <= 1.0, "The probabaility of selecting a node cannot be larger than 1.0!");

  return PopulateRoutingTablesImpl ( 
    net, 
    MakeBoundCallback ( &Ipv4DeflectionRoutingHelper::RandomNodeSelector, selectedPb ) 
    );
}

uint32_t 
Ipv4DeflectionRoutingHelper::PopulateRoutingTablesImpl (
  const BaseTopology& net, const Callback<bool, Ptr<Node> >& selectedNodeCallback)
{
  NS_LOG_FUNCTION ( &net << &selectedNodeCallback  );

  // Obtain the set of all nodes of the topology, and install
  // the deflection routing protocol to each of the node
  //
  // Obtain the set of all nodes
  const NodeContainer& nodes = net.GetNodeContainer ();
  // to count the number of nodes installed with deflection routing
  uint32_t deflectionNNodes = 0;
  // Walk through all the node and install the deflection routing protocol
  for (NodeContainer::Iterator i = nodes.Begin (); i != nodes.End (); i++)
  {
      Ptr<Node> node = *i;

      if (!selectedNodeCallback (node))
      {
        m_nodeSkippedTrace (node);
        NS_LOG_LOGIC ("Skipped adding Ipv4DeflectionRouting protocol to node " << node->GetId ());
        continue;
      }
      deflectionNNodes++;
      m_nodeSelectedTrace (node);

      Ptr<Ipv4DeflectionRouting> deflectionRouting = CreateObject<Ipv4DeflectionRouting> ();
      NS_LOG_LOGIC ("Adding Ipv4DeflectionRouting Protocol to node " << node->GetId ()
        << " with priority " << deflectionRouting->GetPriority ());
      // This is the object that will do deflection routing.  We insert it
      // at slightly higher priority than GlobalRouting and static routing.
      // This means that deflection routes (e.g. host routes) will be consulted
      // before GlobalRoutes and also before static routes
      Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();
      NS_ASSERT_MSG (ipv4, "An Ipv4 protocool object is not found. Ipv4 protocol "
        << "object is needed in order to access the list of routing protocols "
        << "available to the node. Ipv4 object should be aggregated to the node "
        << "during network topology generation.");
      Ptr<Ipv4ListRouting> ipv4ListRouting = DynamicCast<Ipv4ListRouting> (ipv4->GetRoutingProtocol ());
      NS_ASSERT_MSG (ipv4ListRouting, "Ipv4ListRouting protocol object is "
        << "not found. Ipv4ListRouting protocol object is needed in order to add "
        << "a new routing protocol to the node. Ipv4ListRouting object should be "
        << "aggregated to the node during network topology generation.");
      // add the deflectionRouting object to the node
      ipv4ListRouting->AddRoutingProtocol (deflectionRouting, deflectionRouting->GetPriority ());  
      NS_LOG_LOGIC ("deflectionRouting protocol object added to the node-" << node->GetId () 
        << " with priority " << deflectionRouting->GetPriority ());
  }

  return deflectionNNodes;
}

Ipv4ListRoutingHelper
Ipv4DeflectionRoutingHelper::GetIpv4ListRoutingHelper () const
{
  // Ipv4DeflectionRouting needs the Ipv4GlobalRouting for calculating routing tables on all nodes
  Ipv4StaticRoutingHelper staticRouting;
  Ipv4GlobalRoutingHelper globalRouting;
  Ipv4ListRoutingHelper listRouting;
  listRouting.Add (*this, 10);
  listRouting.Add (staticRouting, 0);
  listRouting.Add (globalRouting, -10);

  return listRouting;
}

Ipv4RoutingHelper* 
Ipv4DeflectionRoutingHelper::Copy (void) const
{
  return new Ipv4DeflectionRoutingHelper (*this);
}

Ptr<Ipv4RoutingProtocol> 
Ipv4DeflectionRoutingHelper::Create (Ptr<Node> node) const
{
  stringstream oss;
  oss << "Ipv4DeflectionRouting" << node->GetId ();
  Ptr<Ipv4DeflectionRouting> routing = CreateObject<Ipv4DeflectionRouting> ();
  Names::Add (oss.str (), routing);

  return routing;
}

} // namespace eecityu
} // namespace ns3
